package chain

import (
	"context"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"github.com/ethereum/go-ethereum"
	"github.com/ethereum/go-ethereum/accounts/abi"
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/core/types"
	"go-chain-data/contract"
	"go-chain-data/contract/evmAbi"
	"go-chain-data/global"
	"go-chain-data/mail"
	"go-chain-data/models"
	"go-chain-data/tools"
	"go-chain-data/utils"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"golang.org/x/exp/rand"
	"log"
	"math/big"
	"strconv"
	"strings"
	"time"
)

func getLast() models.ScanConfig {
	var result models.ScanConfig
	scanClient := global.DBEngine.Database(global.DbConfig.DbName).Collection("scan_config")
	filter := bson.D{{"rpcUrl", global.BlockChainConfig.RpcUrl}, {"chainId", global.BlockChainConfig.Id}, {"type", global.BlockChainConfig.Type}, {"chainName", global.BlockChainConfig.Name}}
	err := scanClient.FindOne(context.Background(), filter).Decode(&result)
	if err != nil {
		return models.ScanConfig{}
	}
	return result
}

func InsertMongoData(blockNumber int64, receipts []*types.Receipt) {
	for _, receipt := range receipts {
		for _, rLog := range receipt.Logs {
			if len(rLog.Topics) == 3 && utils.IsTransferEvent(rLog) && models.IsWatchAddressEvent(rLog) && !models.IsExistTxHash(receipt.TxHash.Hex()) {
				insertWatchAddressInfo(blockNumber, receipt.TxHash.Hex(), rLog, false)
			}

			if len(rLog.Topics) == 3 && utils.IsETHTransferEvent(rLog) && models.IsWatchAddressEvent(rLog) && !models.IsExistTxHash(receipt.TxHash.Hex()) {
				insertWatchAddressInfo(blockNumber, receipt.TxHash.Hex(), rLog, true)
			}
			continue
		}
	}
}

func SyncMongoTask() {

	latestBlock := getLast() // 获取数据库中最新的区块信息
	var startNumber int64
	if latestBlock == (models.ScanConfig{}) {
		if global.BlockNumberConfig.Number == "" {
			log.Println("yml文件未配置SettingBlockNumber字段")
			return
		}
		parseInt, err := strconv.ParseInt(global.BlockNumberConfig.Number, 10, 64)
		if err != nil {
			log.Println("blockNumber-string转int64失败:", err)
			return
		}
		startNumber = parseInt
	} else {
		startNumber = int64(latestBlock.LastBlockNumber)
		//这里处理一下 如果是上次意外退出 需要先把startNumber这块的数据删除 在扫块
		err := models.DeleteByBlockNumber(startNumber)
		wai := models.WatchAddressInfo{}
		// 删除watch_address_info表
		err = wai.DeleteByBlockNumber(startNumber)
		if err != nil {
			log.Println("删除失败", err)
			return
		}
	}

	for i := startNumber; true; i++ {
		number, err := global.EthRpcClient.BlockNumber(context.Background())
		if err != nil {
			// 马蹄链专属傻逼错误
			if err.Error() == "server returned non-empty transaction list but block header indicates no transactions" {
				log.Println("server returned non-empty transaction list but block header indicates no transactions ============= blockNumber", i)
				continue
			}
			log.Println("获取最新区块失败, 1秒后重试", err)
			time.Sleep(1 * time.Second)
			continue
		}
		if int64(number)-i <= global.BlockChainConfig.Diff {
			i--
			log.Printf("扫描太快了等一会")
			time.Sleep(2 * time.Second)
			continue
		}

		fmt.Println("当前块:", i)
		block, err := global.EthRpcClient.BlockByNumber(context.Background(), big.NewInt(i))
		if err != nil {
			// 马蹄链专属傻逼错误
			if err.Error() == "server returned non-empty transaction list but block header indicates no transactions" {
				log.Println("server returned non-empty transaction list but block header indicates no transactions ============= blockNumber", i)
				continue
			}
			log.Printf("获取 %s 区块失败: %v", i, err)
			i--
			time.Sleep(1 * time.Second)
			continue
		}
		//hash := block.Hash()
		//num := int64(block.NumberU64())
		//var blockNumberOrHash rpc.BlockNumberOrHash
		//blockNumberOrHash.BlockHash = &hash
		//blockNumberOrHash.BlockNumber = (*rpc.BlockNumber)(&num)
		//blockNumberOrHash.RequireCanonical = true
		//receipts, _ := global.EthRpcClient.BlockReceipts(context.Background(), blockNumberOrHash)
		InsertChainConfig(i)
		//go InsertMongoData(i, receipts)
		go CollectionOperation(i, block.Transactions(), block.Hash().String())
		//time.Sleep(1 * time.Second)
	}
}

// InsertChainConfig 插入区块链配置
func InsertChainConfig(i int64) {
	var scanConfig models.ScanConfig
	chainIdInt := uint(global.BlockChainConfig.Id)
	scanConfig.ChainId = chainIdInt
	scanConfig.ChainName = global.BlockChainConfig.Name
	scanConfig.RpcUrl = global.BlockChainConfig.RpcUrl
	scanConfig.LastBlockNumber = uint64(i)
	scanConfig.Type = global.BlockChainConfig.Type
	scanConfig.UdunId = uint64(global.BlockChainConfig.UdunId)
	err := models.CreateScanConfig(scanConfig)
	if err != nil {
		log.Println("scan_config表insert失败:", err)
		return
	}
}

// 插入监听记录
func insertWatchAddressInfo(blockNumber int64, tHash string, rLog *types.Log, isETH bool) {
	var wai models.WatchAddressInfo
	wai.ChainName = global.BlockChainConfig.Name
	wai.BlockNumber = uint64(blockNumber)
	wai.TxHash = tHash
	// 公链币解析方式和代币不同  判断方式也不同  代币是根据token地址判断  公链币是根据地址判断
	if isETH {
		// 将rLog.Data 截取一半 作为金额 后半部分作是时间戳
		wai.Amount = new(big.Int).SetBytes(rLog.Data[:len(rLog.Data)/2]).String()
		wai.From = common.HexToAddress(rLog.Address.Hex()).String()
		wai.Token = common.HexToAddress(rLog.Topics[1].Hex()).String()
	} else {
		wai.Amount = new(big.Int).SetBytes(rLog.Data).String()
		wai.From = common.HexToAddress(rLog.Topics[1].Hex()).String()
		wai.Token = rLog.Address.String()
	}
	wai.To = common.HexToAddress(rLog.Topics[2].Hex()).String()
	wai.ChainId = uint(global.BlockChainConfig.Id)
	wai.Verify = 0
	wai.CallBackResult = ""
	now := time.Now()
	wai.CallBackTime = now.Unix()
	wai.CallBackNumber = 10
	// 获取需要监听的地址详情
	wac, err := models.GetWatchAddressConfig(wai.From, wai.To, wai.Token)
	if err != nil {
		log.Printf("获取监听地址详情异常: %v", err)
	}
	wai.CallBackUrl = wac.CallBackUrl
	insertedId, err := models.InsertWatchAddressInfo(wai)
	if err != nil {
		log.Printf("failed to insert watch_address_info: %v", err)
	}
	err = tools.SendDappCallBack(wai, wai.CallBackUrl, insertedId)
	if err != nil {
		log.Printf("failed to SendDappCallBack: %v", err)
	}
}

func isMyAddress3(toAddress common.Address, tokenId primitive.ObjectID, addressList []*models.WatchAddressConfig) bool {
	userTokenClient := global.DBEngine.Database(global.DbConfig.DbName).Collection("user_token")
	//addressClient := global.DBEngine.Database(global.DbConfig.DbName).Collection("watch_address_config")
	//find, err := addressClient.Find(context.Background(), bson.D{})
	//if err != nil {
	//	return false
	//}
	//var addressList []*models.WatchAddressConfig
	//err = find.All(context.Background(), &addressList)
	//if err != nil {
	//	return false
	//}
	for _, person := range addressList {
		if person.Address == toAddress.Hex() {
			var userToken models.UserToken
			err := userTokenClient.FindOne(context.Background(), bson.D{{"userId", person.UserId}, {"tokenId", tokenId.Hex()}}).Decode(&userToken)
			if err != nil {
				return false
			}

			return true
		}
	}
	return false
}

func isMyToken(addressList []*models.Token, toAddress string) (bool, primitive.ObjectID) {
	for _, person := range addressList {
		if person.Address == toAddress {
			return true, person.ID
		}
	}

	return false, primitive.NewObjectID()
}

//func isMyToken(addressList []*models.WatchAddressConfig, toAddress string) (bool, string) {
//	for _, person := range addressList {
//		if person.Address == toAddress {
//			return true, person.Token
//		}
//	}
//	return false, ""
//}

func CollectionOperation(blockNumber int64, transactions types.Transactions, blockHash string) {
	if len(transactions) == 0 {
		return
	}
	var address []*models.WatchAddressConfig
	addressClient := global.DBEngine.Database(global.DbConfig.DbName).Collection("watch_address_config")
	filter := bson.D{
		{"chainId", global.BlockChainConfig.Id},
		{"chainName", global.BlockChainConfig.Name},
		{"type", "udun"},
		{"isDel", 0},
	}
	addressData, err := addressClient.Find(context.Background(), filter)
	if err != nil {
		log.Printf("查询监听地址出错: %v", err.Error())
		return
	}
	err = addressData.All(context.Background(), &address)
	if err != nil {
		log.Printf("addressData解析出错: %v", err.Error())
		return
	}

	////获取所有要监控的充币合约地址
	tokenClient := global.DBEngine.Database(global.DbConfig.DbName).Collection("token")
	filter = bson.D{
		{"chainId", global.BlockChainConfig.Id},
		{"chainName", global.BlockChainConfig.Name},
	}
	var tokenList []*models.Token
	tokenData, err := tokenClient.Find(context.Background(), filter)
	if err != nil {
		log.Printf("查询代币地址出错: %v", err.Error())
		return
	}
	err = tokenData.All(context.Background(), &tokenList)
	if err != nil {
		log.Printf("tokenData解析出错: %v", err.Error())
		return
	}
	//abi
	contractAbi := evmAbi.ERC20_ABI
	parsedAbi, err := abi.JSON(strings.NewReader(contractAbi))
	if err != nil {
		log.Printf("Failed to parse contract ABI: %v", err)
		return
	}
	var chainConfig models.ScanConfig
	filters := bson.D{
		{"rpcUrl", global.BlockChainConfig.RpcUrl},
		{"chainId", global.BlockChainConfig.Id},
		{"type", global.BlockChainConfig.Type},
		{"chainName", global.BlockChainConfig.Name},
	}
	err = global.DBEngine.Database(global.DbConfig.DbName).Collection("scan_config").FindOne(context.Background(), filters).Decode(&chainConfig)
	if err != nil {
		log.Printf("查询链信息失败: %v", err)
		return
	}

	upCoinClient := global.DBEngine.Database(global.DbConfig.DbName).Collection("up_coin")
	userClient := global.DBEngine.Database(global.DbConfig.DbName).Collection("user")
	privateKeyClient := global.DBEngine.Database(global.DbConfig.DbName).Collection("user_private_key")

	for _, transaction := range transactions {

		if transaction.To() == nil {
			continue
		}

		var tokenInfoMain models.Token
		err := tokenClient.FindOne(context.Background(), bson.D{{"address", strconv.FormatUint(chainConfig.UdunId, 10)}}).Decode(&tokenInfoMain)
		if err != nil && "mongo: no documents in result" != err.Error() {
			log.Printf("查询主币信息失败 %s", err.Error())
			return
		}
		//从数据库里查询是不是要监听的地址
		if isMyAddress3(common.HexToAddress(transaction.To().Hex()), tokenInfoMain.ID, address) {
			receipt, err := global.EthRpcClient.TransactionReceipt(context.Background(), transaction.Hash())
			if err != nil {
				log.Printf("主币查询交易凭证失败 %s", err.Error())
				continue
			}
			if receipt.Status == 0 {
				log.Printf("交易失败,txHash: %s", receipt.TxHash)
				continue
			}
			//查询用户apikey
			var addressModel models.WatchAddressConfig
			err = addressClient.FindOne(context.Background(), bson.D{{"address", transaction.To().Hex()}, {"type", "udun"}}).Decode(&addressModel)
			if err != nil {
				log.Printf("EVM主币充币监控，查找用户地址详情失败 %s", err.Error())
				continue
			}
			hexId, err := primitive.ObjectIDFromHex(addressModel.UserId)
			if err != nil {
				log.Printf("用户ID string转ObjectId失败 %s", err.Error())
				continue
			}
			var user models.User
			err = userClient.FindOne(context.Background(), bson.D{{"_id", hexId}}).Decode(&user)
			if err != nil {
				log.Printf("EVM主币充币监控，查找用户详情失败 %s", err.Error())
				continue
			}
			var userPrivateKey models.UserPrivateKey
			err = privateKeyClient.FindOne(context.Background(), bson.D{{"userId", addressModel.UserId}, {"mainCoinType", chainConfig.UdunId}}).Decode(&userPrivateKey)

			//1.写入充币记录
			var upCoinList models.UpCoin
			upCoinList.Data = hex.EncodeToString(transaction.Data())
			upCoinList.BlockHash = blockHash
			upCoinList.TxHash = transaction.Hash().Hex()
			signer := types.LatestSignerForChainID(transaction.ChainId())
			from, err := types.Sender(signer, transaction)
			if err != nil {
				log.Printf("EVM主币充币监控，获取from失败 %s", err.Error())
				continue
			}
			upCoinList.From = from.Hex()
			//privateKey, err := crypto.HexToECDSA(userPrivateKey.FeeAddress)
			//if err != nil {
			//	log.Printf("EVM主币Failed to load private key: %s", err)
			//	continue
			//}
			if from.Hex() == common.HexToAddress(userPrivateKey.FeeAddress).Hex() {
				log.Printf("手续费  非充币")
				continue
			}

			divisor := big.NewInt(10).Exp(big.NewInt(10), big.NewInt(int64(tokenInfoMain.Decimals)), nil)
			dividendBig, _ := new(big.Int).SetString(transaction.Value().String(), 10)
			expression := new(big.Rat).SetInt(dividendBig).Quo(new(big.Rat).SetInt(dividendBig), new(big.Rat).SetInt(divisor))
			resultFloat, _ := expression.Float64()
			//转账金额获取且转成字符串
			quotient := strconv.FormatFloat(resultFloat, 'f', -1, 64)

			//当用户的最小归集钱数大于交易金额时，归集
			if userPrivateKey.MinAmount != 0 && resultFloat < userPrivateKey.MinAmount {
				log.Printf("当前用户 %s 的最小归集钱数 %f 小于交易金额 %f", userPrivateKey.UserID, userPrivateKey.MinAmount, resultFloat)
				continue
			}

			upCoinList.To = transaction.To().Hex()
			upCoinList.Value = quotient
			upCoinList.Timestamp = uint(time.Now().Unix())
			upCoinList.BlockNumber = uint(blockNumber)
			upCoinList.TokenAddress = strconv.Itoa(int(chainConfig.UdunId))
			upCoinList.ChainId = uint(transaction.ChainId().Uint64())
			upCoinList.ChainName = chainConfig.ChainName
			upCoinList.SymbolName = tokenInfoMain.Symbol
			upCoinList.UserId = user.Id
			upCoinList.Status = 1
			upCoinId, err := upCoinClient.InsertOne(context.Background(), upCoinList)
			if err != nil {
				log.Printf("EVM主币充币监控，插入充币记录失败 %s", err.Error())
				continue
			}

			// 会员过期时间为0，不进行回调
			if user.Status == 0 || user.MaturityTime == 0 {
				log.Printf("会员已过期，未进行回调")
				continue
			}

			//2 callback发射
			//构建body
			var body models.CallBackBody
			body.Address = upCoinList.To
			body.Amount = upCoinList.Value
			body.Fee = 0
			//查询币种精度
			body.Decimals = int(tokenInfoMain.Decimals)
			body.CoinType = tokenInfoMain.Address
			body.MainCoinType = strconv.Itoa(int(chainConfig.UdunId))
			body.BusinessId = ""
			body.BlockHigh = strconv.Itoa(int(upCoinList.BlockNumber))
			body.Status = 3
			body.TradeId = upCoinList.ID.String()
			body.TradeType = 1
			body.TxId = upCoinList.TxHash
			body.Memo = ""

			rand.Seed(uint64(time.Now().UnixNano())) // 初始化随机数种子
			randomInt := rand.Intn(10000000)         // 生成一个0到99的随机整数

			//回调的请求数据数据
			userApiKey := user.ApiKey
			randomString := strconv.Itoa(randomInt)
			timestamp := strconv.FormatInt(time.Now().Unix(), 10)
			sign, err := tools.GetSign(&body, userApiKey, randomString, timestamp)
			var RequestData models.RequestData
			RequestData.Body = body
			RequestData.Nonce = randomString
			RequestData.Timestamp = timestamp
			RequestData.Sign = sign
			RequestDataStr, err := json.Marshal(RequestData)
			if err != nil {
				log.Printf("充币监控，回调数据转json失败 %s", err.Error())
				continue
			}

			//回调数据
			var callBackData models.CollectionCallback
			callBackData.ToAddress = upCoinList.To
			callBackData.ChainName = global.BlockChainConfig.Name
			callBackData.ChainId = uint(global.BlockChainConfig.Id)
			callBackData.BlockNumber = uint64(uint(blockNumber))
			callBackData.TxHash = transaction.Hash().Hex()
			callBackData.From = from.Hex()
			callBackData.To = transaction.To().Hex()
			callBackData.Token = tokenInfoMain.Address
			callBackData.Amount = quotient
			callBackData.CallBackUrl = addressModel.CallBackUrl
			callBackData.Verify = 0
			callBackData.CallBackTime = time.Now().Unix()
			callBackData.RequestData = string(RequestDataStr)
			callBackData.SymbolName = tokenInfoMain.Name
			callBackData.UserId = user.Id
			insertedId, err := global.DBEngine.Database(global.DbConfig.DbName).Collection("collection_callback").InsertOne(context.Background(), callBackData)
			if err != nil {
				log.Printf("inserted failed to collection_callback table: %s", err.Error())
				continue
			}

			//查看交易金额

			//3.归集
			//计算手续费
			// 获取当前推荐的gas价格
			ctx, _ := context.WithTimeout(context.Background(), time.Minute)
			gasPrice, err := global.EthRpcClient.SuggestGasPrice(ctx)
			if err != nil {
				log.Printf("EVM主币充币监控，归集获取gasPrice失败 %s", err.Error())
				continue
			}

			toAddressGj := common.HexToAddress(userPrivateKey.CollectAddress)
			// 估算调用方法所需的gas限制
			gasLimit, err := global.EthRpcClient.EstimateGas(ctx, ethereum.CallMsg{
				From: common.HexToAddress(upCoinList.To), // 设置调用者地址
				To:   &toAddressGj,
			})

			//计算手续费
			gasValue := gasPrice.Mul(gasPrice, big.NewInt(int64(gasLimit)))

			//给监听地址转手续费
			tx, err := contract.GetTransactionSign(global.EthRpcClient, userPrivateKey.FeeBusinessId, common.HexToAddress(upCoinList.To).Hex(), global.BlockChainConfig.RpcUrl, gasValue.String(), "")

			if err != nil {
				log.Printf("EVM主币fee地址分配手续费失败: %s", err)
				_, err = upCoinClient.UpdateOne(context.Background(), bson.M{"_id": upCoinId.InsertedID.(primitive.ObjectID)}, bson.M{"$set": bson.M{"status": 2}})
				str := "尊敬的BlockWatch会员：\n\n您好！\n\n您的" + global.BlockChainConfig.Name + "链下的手续费私钥余额不足,导致了本次归集失败,请尽快补充公链币,以免影响后续归集.\nDear BlockWatch Member,Your" + global.BlockChainConfig.Name + "chain transaction fee private key balance is insufficient, resulting in a failed collection attempt. Please replenish your public chain coins as soon as possible to avoid affecting subsequent collections."
				options := mail.Options{
					MailHost: global.EmailConfig.Host,
					MailPort: global.EmailConfig.Port,
					MailUser: global.EmailConfig.User,
					MailPass: global.EmailConfig.Password,
					MailTo:   user.Email,
					Subject:  "公链币余额不足提醒",
					Body:     str,
				}
				mail.Send(&options)
				continue
			}

			log.Printf("EVM主币fee地址分配手续费成功: %s", tx)
			//等待手续费到账
			time.Sleep(60 * time.Second)
			//进行归集操作
			var addressInfo models.WatchAddressConfig
			err = addressClient.FindOne(context.Background(), bson.M{"address": upCoinList.To, "type": "udun"}).Decode(&addressInfo)
			if err != nil {
				log.Printf("EVM主币获取地址私钥失败失败: %s", err)
				continue
			}
			//privateKeyUpCoin, err := crypto.HexToECDSA(addressInfo.PrivateKey)
			//if err != nil {
			//	log.Printf("EVM主币Failed to load private key: %s", err)
			//	continue
			//}
			txgj, err := contract.GetTransactionSign(global.EthRpcClient, addressInfo.BusinessId, toAddressGj.Hex(), global.BlockChainConfig.RpcUrl, transaction.Value().String(), "")

			//txgj, err := contract.SendTransactionMain(global.EthRpcClient, privateKeyUpCoin, transaction.Value(), toAddressGj)
			if err != nil {
				log.Printf("EVM主币归集失败: %s", err)
				continue
			}
			log.Printf("EVM主币归集发送成功成功: %s", txgj)

			if addressModel.CallBackUrl != "" {
				//发送post
				go func() {
					err := tools.SendCallBack(&body, sign, randomString, timestamp, addressModel.CallBackUrl, insertedId.InsertedID.(primitive.ObjectID))
					if err != nil {
						log.Printf("充币监控，回调失败 %s", err.Error())
					}
				}()
			}

			_, err = upCoinClient.UpdateOne(context.Background(), bson.M{"_id": upCoinId.InsertedID.(primitive.ObjectID)}, bson.M{"$set": bson.M{"status": 1}})
			if err != nil {
				log.Printf("EVM主币充币监控，更新%s状态失败  %s", upCoinId, err.Error())
				continue
			}

			log.Printf("EVM主币充币监控，更新状态成功")
			continue
		}
		//是否是要监控的代币合约
		//isOk, tokenId := isMyToken(tokenData, transaction.To().Hex())
		isOk, tokenAddress := isMyToken(tokenList, transaction.To().Hex())
		if !isOk {
			continue
		}

		receipt, err := global.EthRpcClient.TransactionReceipt(context.Background(), transaction.Hash())
		if err != nil {
			log.Printf("主币查询交易凭证失败 %s", err.Error())
			continue
		}
		if receipt.Status == 0 {
			log.Printf("交易失败,txHash: %s", receipt.TxHash)
			continue
		}

		//获取token信息
		var tokenInfo models.Token
		err = tokenClient.FindOne(context.Background(), bson.M{"_id": tokenAddress}).Decode(&tokenInfo)
		if err != nil {
			fmt.Printf("获取token信息错误: %v\n", err)
			continue
		}
		//用abi解析data
		method, err := parsedAbi.MethodById(transaction.Data())
		if err != nil {
			log.Printf("Failed to find method: %v", err)
			continue
		}
		//fmt.Println("method: ", method)
		//是否为transfer
		if method.Name != "transfer" {
			continue
		}
		var result struct {
			To     common.Address
			Amount *big.Int
		}
		result1, err := method.Inputs.Unpack(transaction.Data()[4:])
		if err != nil {
			fmt.Printf("解析data错误: %v\n", err)
			continue
		}

		result.To = result1[0].(common.Address)

		result.Amount = result1[1].(*big.Int)

		if isMyAddress3(result.To, tokenInfo.ID, address) {
			log.Printf("进代币充币")
			//查询用户apikey
			var addressModel models.WatchAddressConfig
			err = addressClient.FindOne(context.Background(), bson.D{{"address", result.To.Hex()}, {"type", "udun"}}).Decode(&addressModel)
			if err != nil {
				log.Printf("充币监控，查找用户地址详情失败 %s", err.Error())
				continue
			}
			hexId, err := primitive.ObjectIDFromHex(addressModel.UserId)
			if err != nil {
				log.Printf("用户ID string转ObjectId失败 %s", err.Error())
				continue
			}
			var user models.User
			err = userClient.FindOne(context.Background(), bson.D{{"_id", hexId}}).Decode(&user)
			if err != nil {
				log.Printf("充币监控，查找用户详情失败 %s", err.Error())
				continue
			}

			var userPrivateKey models.UserPrivateKey
			err = privateKeyClient.FindOne(context.Background(), bson.D{{"userId", addressModel.UserId}, {"mainCoinType", chainConfig.UdunId}}).Decode(&userPrivateKey)
			if err != nil {
				log.Printf("Failed to get private key: %s", err)
				continue
			}
			//获取from
			signer := types.LatestSignerForChainID(transaction.ChainId())

			from, err := types.Sender(signer, transaction)

			if err != nil {
				fmt.Printf("获取交易发送者时出错: %v\n", err)
				continue
			}

			divisor := big.NewInt(10).Exp(big.NewInt(10), big.NewInt(int64(tokenInfo.Decimals)), nil)
			dividendBig, _ := new(big.Int).SetString(result.Amount.String(), 10)
			expression := new(big.Rat).SetInt(dividendBig).Quo(new(big.Rat).SetInt(dividendBig), new(big.Rat).SetInt(divisor))
			resultFloat, _ := expression.Float64()
			quotient := strconv.FormatFloat(resultFloat, 'f', -1, 64)

			//当用户的最小归集钱数大于交易金额时，归集
			if userPrivateKey.MinAmount != 0 && resultFloat < userPrivateKey.MinAmount {
				log.Printf("当前用户 %s 的最小归集钱数 %f 小于交易金额 %f", userPrivateKey.UserID, userPrivateKey.MinAmount, resultFloat)
				continue
			}

			//是代币充值
			//1  写入充币记录
			var upCoinList models.UpCoin
			upCoinList.Data = hex.EncodeToString(transaction.Data())
			upCoinList.BlockHash = blockHash
			upCoinList.TxHash = transaction.Hash().Hex()
			upCoinList.From = from.Hex()
			upCoinList.To = result.To.Hex()
			upCoinList.Value = quotient
			upCoinList.Timestamp = uint(time.Now().Unix())
			upCoinList.BlockNumber = uint(blockNumber)
			upCoinList.TokenAddress = transaction.To().Hex()
			upCoinList.ChainId = uint(transaction.ChainId().Int64())
			upCoinList.ChainName = chainConfig.ChainName
			upCoinList.SymbolName = tokenInfo.Symbol
			upCoinList.UserId = user.Id
			upCoinList.Status = 1
			upCoinId, err := upCoinClient.InsertOne(context.Background(), upCoinList)
			if err != nil {
				log.Printf("充币监控，插入充币记录失败 %s", err.Error())
				continue
			}
			// 会员过期时间为0，不进行回调
			if user.Status == 0 || user.MaturityTime == 0 {
				log.Printf("会员已过期，未进行回调")
				continue
			}
			//continue
			//2 callback发射
			//构建body
			var body models.CallBackBody
			body.Address = upCoinList.To
			body.Amount = upCoinList.Value
			body.Fee = 0
			//查询币种精度
			body.Decimals = int(tokenInfo.Decimals)
			body.CoinType = tokenInfo.Address
			body.MainCoinType = strconv.Itoa(int(chainConfig.UdunId))
			body.BusinessId = ""
			body.BlockHigh = strconv.Itoa(int(upCoinList.BlockNumber))
			body.Status = 3
			body.TradeId = upCoinList.ID.String()
			body.TradeType = 1
			body.TxId = upCoinList.TxHash
			body.Memo = ""
			fmt.Println(body, "body")

			rand.Seed(uint64(time.Now().UnixNano())) // 初始化随机数种子
			randomInt := rand.Intn(10000000)         // 生成一个0到99的随机整数

			//回调的请求数据数据
			userApiKey := user.ApiKey
			randomString := strconv.Itoa(randomInt)
			timestamp := strconv.FormatInt(time.Now().Unix(), 10)
			sign, err := tools.GetSign(&body, userApiKey, randomString, timestamp)
			var RequestData models.RequestData
			RequestData.Body = body
			RequestData.Nonce = randomString
			RequestData.Timestamp = timestamp
			RequestData.Sign = sign
			RequestData.UserApikey = userApiKey
			RequestDataStr, err := json.Marshal(RequestData)
			if err != nil {
				log.Printf("充币监控，回调数据转json失败 %s", err.Error())
				continue
			}

			var callBackData models.CollectionCallback
			callBackData.ChainName = global.BlockChainConfig.Name
			callBackData.ChainId = uint(global.BlockChainConfig.Id)
			callBackData.BlockNumber = uint64(uint(blockNumber))
			callBackData.TxHash = transaction.Hash().Hex()
			callBackData.From = from.Hex()
			callBackData.To = transaction.To().Hex()
			callBackData.Token = tokenInfo.Address
			callBackData.Amount = quotient
			callBackData.CallBackUrl = addressModel.CallBackUrl
			callBackData.Verify = 0
			callBackData.CallBackTime = time.Now().Unix()
			callBackData.RequestData = string(RequestDataStr)
			callBackData.SymbolName = tokenInfo.Symbol
			callBackData.ToAddress = upCoinList.To
			callBackData.UserId = user.Id
			insertedId, err := global.DBEngine.Database(global.DbConfig.DbName).Collection("collection_callback").InsertOne(context.Background(), callBackData)
			if err != nil {
				log.Printf("inserted failed to collection_callback table: %s", err.Error())
				continue
			}

			//3当场归集
			//计算手续费
			// 获取当前推荐的gas价格
			ctx, _ := context.WithTimeout(context.Background(), time.Minute)
			gasPrice, err := global.EthRpcClient.SuggestGasPrice(ctx)
			if err != nil {
				log.Printf("充币监控，归集获取gasPrice失败 %s", err.Error())
				continue
			}

			// 解析合约ABI
			parsedABI, err := abi.JSON(strings.NewReader(evmAbi.ERC20_ABI))
			if err != nil {
				log.Printf("充币监控，归集解析abi失败 %s", err.Error())
				continue
			}
			// 打包要调用的合约方法和参数

			input, err := parsedABI.Pack("transfer", common.HexToAddress(userPrivateKey.CollectAddress), result.Amount)
			if err != nil {
				log.Printf("充币监控，归集打包交易数据失败 %s", err.Error())
				continue
			}
			// 估算调用方法所需的gas限制
			gasLimit, err := global.EthRpcClient.EstimateGas(ctx, ethereum.CallMsg{
				From: common.HexToAddress(upCoinList.To), // 设置调用者地址
				To:   transaction.To(),
				Data: input,
			})

			//计算手续费
			gasValue := gasPrice.Mul(gasPrice, big.NewInt(int64(gasLimit)))

			//从手续费地址  转充币地址
			//privateKey, err := crypto.HexToECDSA(userPrivateKey.FeeAddress)
			//if err != nil {
			//	log.Printf("Failed to load private key: %s", err)
			//	continue
			//}

			//tx, err := contract.SendTransactionMain(global.EthRpcClient, privateKey, gasValue, common.HexToAddress(upCoinList.To))
			tx, err := contract.GetTransactionSign(global.EthRpcClient, userPrivateKey.FeeBusinessId, common.HexToAddress(upCoinList.To).Hex(), global.BlockChainConfig.RpcUrl, gasValue.String(), "")

			if err != nil {
				log.Printf("fee地址分配手续费失败: %s", err)
				_, err = upCoinClient.UpdateOne(context.Background(), bson.M{"_id": upCoinId.InsertedID.(primitive.ObjectID)}, bson.M{"$set": bson.M{"status": 2}})
				str := "尊敬的BlockWatch会员：\n\n您好！\n\n您的" + global.BlockChainConfig.Name + "链下的手续费私钥余额不足,导致了本次归集失败,请尽快补充公链币,以免影响后续归集.\nDear BlockWatch Member,Your" + global.BlockChainConfig.Name + "chain transaction fee private key balance is insufficient, resulting in a failed collection attempt. Please replenish your public chain coins as soon as possible to avoid affecting subsequent collections."
				options := mail.Options{
					MailHost: global.EmailConfig.Host,
					MailPort: global.EmailConfig.Port,
					MailUser: global.EmailConfig.User,
					MailPass: global.EmailConfig.Password,
					MailTo:   user.Email,
					Subject:  "公链币余额不足提醒",
					Body:     str,
				}
				mail.Send(&options)
				continue
			}

			log.Printf("fee地址分配手续费成功: %s", tx)
			//等待手续费到账
			time.Sleep(60 * time.Second)
			//进行归集操作
			fmt.Println("进入归集方法")
			var addressInfo models.WatchAddressConfig
			err = addressClient.FindOne(context.Background(), bson.M{"address": result.To.Hex(), "type": "udun"}).Decode(&addressInfo)
			if err != nil {
				log.Printf("获取地址私钥失败失败: %s", err)
				continue
			}
			//privateKeyUpCoin, err := crypto.HexToECDSA(addressInfo.PrivateKey)
			//if err != nil {
			//	log.Printf("Failed to load private key: %s", err)
			//	continue
			//}

			//获取账户余额--防止代币有滑点
			realAmount, err := getUserAccount(tokenInfo.Address, result.To.Hex())
			if err != nil {
				log.Println("获取用户代币余额失败:", err)
				continue
			}
			txgj, err := contract.GetTransactionSign(global.EthRpcClient, addressInfo.BusinessId, common.HexToAddress(userPrivateKey.CollectAddress).Hex(), global.BlockChainConfig.RpcUrl, realAmount.String(), common.HexToAddress(transaction.To().Hex()).Hex())

			//txgj, err := contract.SandToken(global.EthRpcClient, privateKeyUpCoin, common.HexToAddress(transaction.To().Hex()), common.HexToAddress(userPrivateKey.CollectAddress), realAmount)
			if err != nil {
				log.Printf("归集失败: %s", err)
				continue
			}
			log.Printf("归集发送成功成功: %s", txgj)

			if addressModel.CallBackUrl != "" {
				//发送post
				go func() {
					err := tools.SendCallBack(&body, sign, randomString, timestamp, addressModel.CallBackUrl, insertedId.InsertedID.(primitive.ObjectID))
					if err != nil {
						log.Printf("充币监控，发送回调失败 %s", err)
					}
				}()
			}

			_, err = upCoinClient.UpdateOne(context.Background(), bson.M{"_id": upCoinId.InsertedID.(primitive.ObjectID)}, bson.M{"$set": bson.M{"status": 1}})
			if err != nil {
				log.Printf("EVM主币充币监控，更新%s状态失败  %s", upCoinId, err.Error())
				continue
			}

			log.Printf("EVM主币充币监控，更新状态成功")

		}

	}
	log.Printf("区块高度: %v, 交易数量: %v， Udun处理完成", blockNumber, len(transactions))
}

func getUserAccount(token string, account string) (*big.Int, error) {

	// 代币合约地址
	tokenAddress := common.HexToAddress(token)

	// 账户地址
	accountAddress := common.HexToAddress(account)

	// 解析 ERC20 合约 ABI
	parsedABI, err := abi.JSON(strings.NewReader(evmAbi.ERC20_ABI))
	if err != nil {
		return big.NewInt(0), err
	}

	// 调用 balanceOf 方法
	data, err := parsedABI.Pack("balanceOf", accountAddress)
	if err != nil {
		return big.NewInt(0), err
	}

	// 创建调用消息
	msg := ethereum.CallMsg{
		To:   &tokenAddress,
		Data: data,
	}

	// 发送调用消息
	result, err := global.EthRpcClient.CallContract(context.Background(), msg, nil)
	if err != nil {
		return big.NewInt(0), err
	}

	// 解析返回结果
	balance := new(big.Int)
	err = parsedABI.UnpackIntoInterface(&balance, "balanceOf", result)
	if err != nil {
		return big.NewInt(0), err
	}
	fmt.Printf("账户地址%s里的余额是: %s\n", account, balance.String())
	return balance, nil
}
